#!/usr/bin/env python
#
# testing for keep-alive connection
#   agent-pool support
#

import base64


class   Packet(object)  :

    MIN_PACKET_ID = 1
    MAX_PACKET_ID = 65535
    packet_id_current = MAX_PACKET_ID

    ACTION_REQUEST  = 'REQUEST'
    ACTION_RESPONSE = 'RESPONSE'

    OP_APPEND = 'APPEND'
    OP_INSERT = 'INSERT'
    OP_UPDATE = 'UPDATE'
    OP_REPLACE = 'REPLACE'
    OP_ERASE  = 'ERASE'
    OP_FIND   = 'FIND'

    RC_OK = 200

    RC_ERROR = 300

    RC_BAD_ROUTE = 400

    def __init__(self) :
        self.clear()
        ## __init__()

    def clear(self) :
        self.action = None
        self.table = None
        self.op = None
        self.key = None
        self.data = None
        self.error = None
        self.state = 0
        self.packid = 0
        self.respid = 0
        ## clear()

    def __str__(self) :
        s = "action:%s table:%s op:%s key:%s packid:%d" % (self.action, self.table, self.op, self.key, self.packid)
        if self.respid > 0 :
            s += " respid:%d" % self.respid
        if self.state :
            s += ' state:%d' % self.state

        if self.data :
            s += ' data:%s' % self.data
        if self.error :
            s += ' error:%s' % self.error
        return s
        ## __str__()

    def isComplete(self) :
        if not self.action or not self.table or not self.op or not self.key :
            return  False
        return  True

    def setRequest(self) :
        if self.ACTION_REQUEST == self.action : return
        self.action = self.ACTION_REQUEST
    def setResponse(self) :
        if self.ACTION_RESPONSE == self.action : return
        self.action = self.ACTION_RESPONSE

    def isRequest(self) :
        if self.ACTION_REQUEST == self.action : return True
        return  False
    def isResponse(self) :
        if self.ACTION_RESPONSE == self.action : return True
        return  False

    def isOk(self) :
        return self.state == self.RC_OK
        ## isOk()

    ####-----------------------------------------------------------####

    def depack(cls, record) :
        '''decode packet record string into Packet struct'''
        field_separator = '\t'
        kv_separator = ':'
        packet = Packet()
        fields = record.split(field_separator)
        for f in fields :
            (key, value) = f.split(kv_separator, 1)
            if not value : continue
            if 'action' == key :
                packet.action = value
            elif 'table' == key :
                packet.table = value
            elif 'op' == key :
                packet.op = value
            elif 'key' == key :
                packet.key = base64.b64decode(value)
            elif 'data' == key :
                packet.data = base64.b64decode(value)
            elif 'error' == key :
                packet.error = base64.b64decode(value)
            elif 'state' == key :
                packet.state = int(value)
            elif 'packid' == key :
                packet.packid = int(value)
            elif 'respid' == key :
                packet.respid = int(value)

        if not packet.isComplete() : return None
        return  packet
        # depack()
    depack = classmethod(depack)

    def enpack(cls, packet) :
        '''encode Packet struct into packet record string'''
        packid = packet.packid
        if not packid :  packid = cls.nextPacketID()
        fields = []
        fields.append('action:%s' % packet.action)
        fields.append('table:%s' % packet.table)
        fields.append('packid:%d' % packid)
        if packet.respid > 0 :
            fields.append('respid:%d' % packet.respid)
        fields.append('op:%s' % packet.op)
        fields.append('state:%d' % packet.state)
        fields.append(':'.join(['key', base64.b64encode(packet.key)]))
        if packet.data :
            fields.append(':'.join(['data', base64.b64encode(packet.data)]))
        if packet.error :
            fields.append(':'.join(['error', base64.b64encode(packet.error)]))
        record = '\t'.join(fields)
        return  (record, packid)
        ## enpack()
    enpack = classmethod(enpack)

    def nextPacketID(cls) :
        if cls.packet_id_current == cls.MAX_PACKET_ID :
            cls.packet_id_current = cls.MIN_PACKET_ID
        else :
            cls.packet_id_current += 1
        return  cls.packet_id_current
        ## nextPacketID()
    nextPacketID = classmethod(nextPacketID)

    ## class Packet


#######################################################################


class   PacketTuple(object)  :

    MIN_PACKET_ID = 1
    MAX_PACKET_ID = 65535
    packet_id_current = MAX_PACKET_ID

    ACTION_REQUEST  = 'REQUEST'
    ACTION_RESPONSE = 'RESPONSE'

    OP_APPEND = 'APPEND'
    OP_INSERT = 'INSERT'
    OP_UPDATE = 'UPDATE'
    OP_REPLACE = 'REPLACE'
    OP_ERASE  = 'ERASE'
    OP_FIND   = 'FIND'

    def __init__(self) :
        self.clear()
        ## __init__()

    def clear(self) :
        #            action table op    key   data  error state packid respid
        self._list = [None, None, None, None, '',   '',    0,    0,     0]
        ## clear()

    def setAction(self, action) :
        self._list[0] = action
    def getAction(self) :
        return self._list[0]
    action = property(getAction, setAction)

    def setTable(self, table) :
        self._list[1] = table
    def getTable(self) :
        return self._list[1]
    table = property(getTable, setTable)

    def setOp(self, op) : 
        self._list[2] = op
    def getOp(self) :
        return self._list[2]
    op = property(getOp, setOp)

    def setKey(self, key) :
        self._list[3] = key
    def getKey(self) :
        return self._list[3]
    key = property(getKey, setKey)

    def setData(self, data) :
        self._list[4] = data
    def getData(self) :
        return self._list[4]
    data = property(getData, setData)

    def setError(self, error) :
        self._list[5] = error
    def getError(self) :
        return self._list[5]

    def setState(self, state) :
        self._list[6] = state
    def getState(self) :
        return self._list[6]

    def enpack(cls, packet) :
        to_pack = [ str(x) for x in packet._list ]
        to_pack[ 3 ] = base64.b64encode( to_pack[3] )
        to_pack[ 4 ] = base64.b64encode( to_pack[4] )
        to_pack[ 5 ] = base64.b64encode( to_pack[5] )

        record = '\t'.join(to_pack)
        return (record,0)
        ## enpack()
    enpack = classmethod(enpack)

    def depack(cls, record) :
        pack = []
        pack.extend( record.split('\t') )

        pack[ 3 ] = base64.b64decode( pack[3] )
        pack[ 4 ] = base64.b64decode( pack[4] )
        pack[ 5 ] = base64.b64decode( pack[5] )
        pack[ 6 ] = int( pack[6] )
        pack[ 7 ] = int( pack[7] )
        pack[ 8 ] = int( pack[8] )
        return pack
        ## depack()
    depack = classmethod(depack)

    ## class Packet


